<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>儿童编程</title>
</head>

<body>
<canvas id='canvas' width='800' height='600' style='width:800px;height:600px;border:1px solid #058;'></canvas>
<textarea id='program' cols='40' rows='8' >
{
x=4;
repeat(5){
show(x);
x=x+1;
}
}
</textarea>
<button onclick='parse_program();'>分析词法</button>
<textarea id='parse' cols='40' rows='8' >
</textarea>
<img src='image/1.JPG' id='cd'/>
<img src='image/2.JPG' id='sz'/>
<img src='image/3.JPG'/>
<img src='image/4.PNG'/>
<script src="common.js" type="text/javascript"></script>
<script>
//关键词列表
var keyword=['if','else','repeat'];
var sysfunc=['go','toleft','toright','jump','read','write','check','remove','color'];
//指针
var pindex=0;
//程序代码
var program='';
//全局变量,第一行存放变量名称，第二行存放变量值
var variable=[[],[]];
//字符集
var charset=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','-','*','/','=','!','>','<','{','}',';','(',')',' ','\r','\n','\t'];
//读取的符号类型
var symtype=0;//1==自定义标识符   2==数值   3==赋值   4==关系运算   5==算术运算   6==语句结束   7==小括号  8==大括号   9==关键字   10==系统函数
//当前读取的符号
var symbol='';
//错误信息
var errorinfo=[];
/*目标代码
调用函数   CALL 函数名 参数表达式
设置变量   SET  变量名 表达式
为0时跳转  JPC  地址   条件表达式
跳转      JMP  地址
*/
var target_pg=[];

//地图数据
var map={
mapwidth:6,mapheight:3,
map:[[23,23,23,23,23,23],
	[8,8,8,8,8,8],
	[23,23,23,23,23,23]],
startx:0,starty:1,
startori:2,
endx:5,endy:1,
endori:2,
success:1
};


var canvas=document.getElementById('canvas');
var ctx=canvas.getContext("2d");
var img=new Array();
img[0]=document.getElementById('sz');
img[1]=document.getElementById('cd');
//window.onload=draw_map(map);//图片未完成加载,导致drawImage失败

document.onreadystatechange = stateChange;

function stateChange() {
	if(document.readyState == 'complete') {
		draw_map(map);
	}
}

function getch(){
	if(pindex>program.length-1) return '';
	var ch=program.charAt(pindex);
	pindex++;
	return ch;
}

function getsym(){
	var ch=getch();
	if(ch=='') return '';
	if(charset.indexOf(ch)==-1){//读到不合格的字符
		error('含有不合格的字符：'+ch);
		return '';	
	}
	while(checksplit(ch)){
		ch=getch();
	}
	//1==标识符   2==数值   3==赋值   4==关系运算   5==算术运算   6==语句结束   7==小括号  8==大括号
	var sym='';
	if(checkletter(ch)){//字母
		while(checkletter(ch)){
			sym+=ch;
			ch=getch();	
		}
		if(ch!='') pindex--;
		symtype=1;
		symbol=sym;
		return;
	}else if(checknumber(ch)){
		while(checknumber(ch)){
			sym+=ch;
			ch=getch();	
		}
		if(ch!='') pindex--;
		symtype=1;
		symbol=sym;
		return ;
	}else if(ch=='=' || ch=='>' || ch=='<'){
		var ch1=getch();
		if(ch1=='=') {
			symtype=4;
			symbol=ch+ch1;
			return;
		}
		pindex--;//回退一个字符
		if(ch=='=') symtype=3; else symtype=4;
		symbol=ch;
		return;
	}else if(ch=='!'){
		var ch1=getch();
		if(ch1!='=') {
			error('！的后面需要跟着=');
			return '';
		}
		symtype=4;
		symbol='!=';
		return;
	}else if(ch==')' || ch=='('){
		symtype=7;
		symbol=ch;
		return ;
	}else if(ch=='}' || ch=='{'){
		symtype=8;
		symbol=ch;
		return;	
	}else if(ch==';'){
		symtype=6;
		symbol=ch;
		return ;
	}
	symtype=5;
	symbol=ch;
}

function parse_program(){
	pindex=0;
	errorinfo=[];
	target_pg.length=0;
	program=document.getElementById('program').value;
	program=program.replace(/}/g,'};');
	getsym();
	parse_statement();
	if(errorinfo.length<=0) alert('程序没有错误！');
	console.log(target_pg);
	if(errorinfo.length<=0) interpret();
}

//分析代码组
function parse_statement(){
	var parse_text=document.getElementById('parse');
	//alert(symbol);
		parse_text.value=parse_text.value+symbol+'\n';
		//alert(symbol);
		//开始词法分析
		//1==标识符   2==数值   3==赋值   4==关系运算   5==算术运算   6==语句结束   7==小括号  8==大括号
		switch(symtype){
			case 1:
				switch(symbol){
					case 'if':
						parse_if();
						break;
					case 'repeat':
						parse_repeat();
						break;
					default:
						var ident=symbol;
						getsym();
						if(symbol=='('){//解释函数
							var p1=pindex;
							getsym();
							var op2='';
							if(symbol!=')'){
								
								parse_expression();
								op2=program.substr(p1,pindex-p1-1);
							}
							if(symbol!=')') {
								error('函数中(必须和)配对');
								return -1;	
							}
							var pg={code:'call',op1:ident,op2:op2};
							target_pg.push(pg);
							getsym();
						}else if(symbol=='='){
							var p1=pindex;
							getsym();
							parse_expression();
							var op2=program.substr(p1,pindex-p1-1);
							var pg={code:'set',op1:ident,op2:op2};
							target_pg.push(pg);
						}else {error('标识符后面需要跟上(或=');return;}
						break;	
				}
				break;
			case 6://遇到; 直接进入下一个语句
				getsym();
				return;
			case 8:
				if(symbol=='{'){
					getsym();
					if(symbol=='}') {getsym();return;}//空语句组
					parse_statement();
					while(symbol==';'){//有多个语句
						getsym();
						if(symbol=='}'){break;}
						parse_statement();	
					}
					
					if(symbol!='}') {error('{}一定要配对');return;}
					getsym();
				}
				break;
			default://表达式
				error('发生错误');
				return;
				break;	
		}
}
//分析if语句
function parse_if(){
	//记录下当前代码位置
	var pgi=target_pg.length;
	
	getsym();
	if(symbol!='(') {
		error('if后面必须跟上(');
		return -1;	
	}
	var p1=pindex;
	parse_condition();
	var op2=program.substr(p1,pindex-p1-1);
	//插入代码
	target_pg.push({code:'jpc',op1:'',op2:op2});
	if(symbol!=')') {
		error('if中(必须和)配对');
		return -1;	
	}
	getsym();
	parse_statement();
	target_pg[pgi].op1=target_pg.length;
}

function parse_condition(){
	getsym();
	parse_expression();
	if(symtype!=4){
		error('条件运算时，中间必须是== != > < >= <=');
		return -1;	
	}
	getsym();
	parse_expression();
	
}

function parse_repeat(){
	
	getsym();
	if(symbol!='(') {
		error('repeat后面必须跟上(');
		return -1;	
	}
	var p1=pindex;
	getsym();
	parse_expression();
	var op2=program.substr(p1,pindex-p1-1);
	//产生一个临时变量
	var temp_var='temp'+Math.floor(Math.random()*1000);
	target_pg.push({code:'set',op1:temp_var,op2:op2});
	var pgi=target_pg.length;//记录当前代码位置
	op2=temp_var+'>0';
	target_pg.push({code:'jpc',op1:'',op2:op2});
	if(symbol!=')') {
		error('repeat中(必须和)配对');
		return -1;	
	}
	getsym();
	parse_statement();
	target_pg.push({code:'set',op1:temp_var,op2:temp_var+'-1'});
	target_pg.push({code:'jmp',op1:pgi,op2:''});
	target_pg[pgi].op1=target_pg.length;
}


function parse_expression(){	
	parse_term();
	while(symbol=='+' || symbol=='-'){
		getsym();
		parse_term();
	}
}

function parse_term(){
	parse_factor();
	while(symbol=='*' || symbol=='/') {
		getsym();
		parse_factor();
	}
}

function parse_factor(){
	if(symtype==1){
		getsym();
		return ;
	}else if(symtype==2){
		getsym();
		return;
	}else	if(symbol=='('){
		getsym();
		parse_expression();
		if(symbol!=')') {
			error('表达式中的括号一定要匹配');
			return -1;	
		}
		getsym();
	}else{
		error('错误的表达式');
		return -1;
	}
}

function error(str){
	errorinfo[errorinfo.length]=str;
	alert(str);
}

//执行中间代码
function interpret(){
	var i=0;
	while(i<target_pg.length){
		var pg=target_pg[i];
		if(pg.code=='set'){
			var t=expression_value(pg.op2);
			//alert(pg.op1+'='+t+','+pg.op2);
			set_variable(pg.op1,t);
			i++;
		}else if(pg.code=='jpc'){
			var c=get_condition(pg.op2);
			if(c){//条件成立，执行下一条语句
				i++;
			}else{
				i=pg.op1;
			}
		}else if(pg.code=='jmp'){
			i=pg.op1;
		}else if(pg.code=='call'){
			switch(pg.op1){
				case 'show':
					var v=expression_value(pg.op2);
					alert(v);
					
					break;	
			}
			i++;
		}
	}
	alert('程序执行完成');
}

function get_condition(cond){
	var a,b;
	if(cond.indexOf('==')!=-1) {
		a=cond.split('==');
		b='==';
	}
	else if(cond.indexOf('>=')!=-1) {a=cond.split('>=');b='>=';}
	else if(cond.indexOf('<=')!=-1) {a=cond.split('<=');b='<=';}
	else if(cond.indexOf('>')!=-1) {a=cond.split('>');b='>';}
	else if(cond.indexOf('<')!=-1) {a=cond.split('<');b='<';}
	if(a.length=3){
		var v1=expression_value(a[0]);
		var v2=expression_value(a[1]);
		switch(b){
			case '==':return v1==v2;
			case '>=':return v1>=v2;
			case '<=':return v1<=v2;
			case '>':return v1>v2;
			case '<':return v1<v2;	
		}
	}else{
		alert('条件表达式错误');	
	}
}

function draw_map(map){
	//console.log(map.mapheight);
	for(var i=0;i<map.mapwidth;i++){
		for(var j=0;j<map.mapheight;j++){
			switch(map.map[j][i]){
			case 23:
				ctx.drawImage(img[0],i*64,j*64);
				break;
			case 8:
				ctx.drawImage(img[1],i*64,j*64);
				break;	
			}
		}
		
	}
}
</script>
</body>
</html>
